package com.matt.service.care.impl;

import com.matt.commons.exception.BusinessException;
import com.matt.domain.Children;
import com.matt.domain.care.HLCareBaseRecord;
import com.matt.domain.care.HLFeedingRecord;
import com.matt.foundation.model.DailyRange;
import com.matt.foundation.model.ErrorCode;
import com.matt.foundation.utils.CopyUtils;
import com.matt.service.ChildrenService;
import com.matt.service.care.HLCareBaseService;
import com.matt.service.care.HLDailyRecordService;
import com.matt.service.care.po.HLCareBaseRecordPO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class HLCareBaseServiceImpl<T1 extends HLCareBaseRecord, T2 extends JpaSpecificationExecutor<T1> & JpaRepository<T1,String>, T3 extends HLCareBaseRecordPO>
        implements HLCareBaseService<T1, T3> {

    T2 baseRepository;
    public void setBaseRepository(T2 baseRepository) {
        this.baseRepository = baseRepository;
    }

    @Autowired
    ChildrenService childrenService;

    @Autowired
    HLDailyRecordService dailyRecordService;

    private Children permissionCheck(String areaId, String childId){
        Children c = childrenService.getByIdAndAreaId(childId, areaId);
        if (c == null){
            throw new BusinessException(ErrorCode.CHILDREN_NOT_EXIST, "宝贝不存在");
        }
        return c;
    }

    @Override
    public T1 get(String areaId, String staffId, String childId, String id) {
        permissionCheck(areaId, childId);

        T1 t = baseRepository.getOne(id);
        if (t == null || !t.getChildId().equals(childId) || t.getDeleted()){
            return null;
        }
        return t;
    }

    @Override
    public Page<T1> page(String areaId, String staffId, String childId, PageRequest pageRequest) {
        permissionCheck(areaId, childId);

        Specification<T1> specification = new Specification<T1>() {
            @Override
            public Predicate toPredicate(Root<T1> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                Predicate p1 = criteriaBuilder.and(criteriaBuilder.equal(root.get("childId"), childId), criteriaBuilder.equal(root.get("deleted"), 0));
                query.where(p1);
                return query.getRestriction();
            }
        };

        Page<T1> page = baseRepository.findAll(specification, pageRequest);

        return page;
    }


    @Override
    public List<T1> dailyRecords(String areaId, String staffId, String[] childIds, Date daily, Map<String, Object> params){
        if (daily == null || childIds.length == 0){
            throw new BusinessException(ErrorCode.PARAM_CAN_NOT_BLANK, "参数错误");
        }

        List<Children>  childrens = childrenService.getByIds(childIds, areaId);

        if (childrens.size() != childIds.length ){
            throw new BusinessException(ErrorCode.CHILDREN_NOT_EXIST, "指定宝宝不存在");
        }


        //计算daily时间
        DailyRange dr = DailyRange.of(daily);

        Specification<T1> specification = new Specification<T1>() {
            @Override
            public Predicate toPredicate(Root<T1> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

                CriteriaBuilder.In<Object> p4 = criteriaBuilder.in(root.get("childId"));
                for (String id : childIds){
                    p4.value(id);
                }

                Predicate p3 = criteriaBuilder.equal(root.get("deleted"), 0);
                Predicate p2 = criteriaBuilder.between(root.get("eventTime"), dr.getDayBegin(), dr.getDayEnd());
                Predicate p1 = criteriaBuilder.and(p2, p3, p4 );
                if (params.get("diaper") != null){
                    Predicate  p5 = criteriaBuilder.equal(root.get("diaper"), params.get("diaper"));
                    p1 = criteriaBuilder.and(p2, p3, p4 ,p5);
                    query.where(p1);
                }else {
                    query.where(p1);
                }
                query.orderBy(criteriaBuilder.desc(root.get("eventTime")));
                return query.getRestriction();
            }
        };

        List<T1> dailyRecords = baseRepository.findAll(specification);
        return dailyRecords;
    }

    @Override
    public List<T1> dailyRecords(String areaId, String staffId, String childId, Date daily, Map<String, Object> params){
        if (daily == null){
            throw new BusinessException(ErrorCode.PARAM_CAN_NOT_BLANK, "参数错误");
        }

        permissionCheck(areaId, childId);

        //计算daily时间
        DailyRange dr = DailyRange.of(daily);

        Specification<T1> specification = new Specification<T1>() {
            @Override
            public Predicate toPredicate(Root<T1> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                Predicate p4 = criteriaBuilder.equal(root.get("childId"), childId);
                Predicate p3 = criteriaBuilder.equal(root.get("deleted"), 0);
                Predicate p2 = criteriaBuilder.between(root.get("eventTime"), dr.getDayBegin(), dr.getDayEnd());
                Predicate p1 = criteriaBuilder.and(p2, p3, p4 );
                if (params.get("diaper") != null){
                    Predicate  p5 = criteriaBuilder.equal(root.get("diaper"), params.get("diaper"));
                    p1 = criteriaBuilder.and(p2, p3, p4 ,p5);
                    query.where(p1);
                }else {
                    query.where(p1);
                }
                query.orderBy(criteriaBuilder.desc(root.get("eventTime")));
                return query.getRestriction();
            }
        };

        List<T1> dailyRecords = baseRepository.findAll(specification);
        return dailyRecords;
    }

    @Override
    public List<T1> childrenIdListDailyRecords(String areaId, List<String> childrenIdList, Date begin, Date end, Map<String, Object> params) {
        Specification<T1> specification = new Specification<T1>() {
            @Override
            public Predicate toPredicate(Root<T1> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                CriteriaBuilder.In<Object> p4 = criteriaBuilder.in(root.get("childId"));

                String[] childIds = childrenIdList.toArray(new String[childrenIdList.size()]);
                for (String id : childIds){
                    p4.value(id);
                }
                Predicate p3 = criteriaBuilder.equal(root.get("deleted"), 0);
                Predicate p2 = criteriaBuilder.between(root.get("eventTime"),begin, end);
                Predicate p1 = criteriaBuilder.and(p2, p3, p4 );
                if (params != null && params.get("diaper") != null){
                    Predicate  p5 = criteriaBuilder.equal(root.get("diaper"), params.get("diaper"));
                    p1 = criteriaBuilder.and(p2, p3, p4 ,p5);
                    query.where(p1);
                }else {
                    query.where(p1);
                }
                query.orderBy(criteriaBuilder.desc(root.get("eventTime")));
                return query.getRestriction();
            }
        };

        List<T1> dailyRecords = baseRepository.findAll(specification);
        return dailyRecords;
    }

    @Override
    public void delete(String areaId, String staffId, String childId, String id) {
        T1 t = get(areaId, staffId, childId, id);

        if (t == null){
            throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "记录不存在");
        }

        t.setDeleted(true);

        baseRepository.save(t);
    }

    @Override
    public T1 add(String areaId, String staffId, T3 param) {
        ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();
        Class clazz = (Class<T1>) ptype.getActualTypeArguments()[0];

        try {
            T1 o = null;
            if (StringUtils.isNotBlank(param.getId())){
                o = baseRepository.getOne(param.getId());

                if(!o.getAreaId().equals(areaId)){
                    throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "指定对象不存在");
                }
            }else {
                o = (T1) clazz.newInstance();

                Children c = permissionCheck(areaId, param.getChildId());

                o.setChildName(c.getName());
                o.setCreatedBy(staffId);
                o.setAreaId(areaId);
                o.setCreated(new Date());
            }

            String[] skips = {"id","childIds"};
            CopyUtils.copyPropertiesSkipNullAndExternalProperties(param, o, skips);

            baseRepository.save(o);

            if (StringUtils.isBlank(param.getId())) {
                dailyRecordService.markCareRecord(staffId, o);
                dailyRecordService.pushCareRecord(staffId, o);
            }

            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        throw new BusinessException(ErrorCode.UNDEFINED, "模板对象错误");
    }

    @Override
    @Transactional
    public List<T1> addAll(String areaId, String staffId, T3 param) {
        ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();
        Class clazz = (Class<T1>) ptype.getActualTypeArguments()[0];

        try {

            List<Children> cs = childrenService.getByIds(param.getChildIds(), areaId);

            Map<String, Children> cache = cs.stream().collect(Collectors.toMap(Children::getId, o -> o));

            if (cs == null || cs.size() == 0 || param.getChildIds() == null || cs.size() != param.getChildIds().length){
                throw new BusinessException(ErrorCode.SUCCESSFUL, "宝宝没找到");
            }

            List<T1> batch = new ArrayList<>();
            for (String childId : param.getChildIds()){

                T1 o = (T1) clazz.newInstance();

                BeanUtils.copyProperties(param, o, CopyUtils.getNullPropertyNames(param));
                // 如果 T1 是 feed，则保存其 ID 以便后续使用
//                if (clazz.equals(HLFeedingRecord.class)) {
//                    HLFeedingRecord feed = (HLFeedingRecord) o;
//                    o.setId(feed.getId()); // 确保 ID 被设置
//                } else {
//                    o.setId(null); // 对于其他类型的实体，保持默认行为
//                }
                o.setId(null); // 对于其他类型的实体，保持默认行为
                o.setChildId(childId);
                o.setChildName(cache.get(childId).getName());
                o.setCreatedBy(staffId);
                o.setAreaId(areaId);
                o.setCreated(new Date());

                batch.add(o);
            }

            baseRepository.saveAll(batch);

            dailyRecordService.markCareRecords(staffId, (List<HLCareBaseRecord>) batch);

            //发送通知
            dailyRecordService.pushCareRecords(staffId, (List<HLCareBaseRecord>) batch);

            return batch;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        throw new BusinessException(ErrorCode.UNDEFINED, "模板对象错误");
    }
}
